#ifdef KITSCHY_DEBUG_MEMORY 
#include "debug_memorymanager.h"
#endif

#ifdef WIN32
#include "windows.h"
#endif

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"
#include "pthread.h"

#include "BList.h"
#include "BVector.h"
#include "geometrics.h"
#include "quaternion.h"
#include "symbol.h"
#include "sort.h"
#include "ontology.h"
#include "featureterm.h"
#include "FTKBase.h"

#include "AWEntity.h"
#include "AWMessage.h"
#include "AWBrain.h"
#include "AWTracerBrain.h"
#include "AWThreadedBrain.h"
#include "AWNOOSBrain.h"
#include "AWNOOSLearnerBrain.h"

#include "CaseBaseUtils.h"

#include "psm.h"

#include "loader.h"



// Header of the modified LID version adapted to generate counterarguments:
Prediction *eva_LID_argumentation(FeatureTerm *problem,List<FeatureTerm> *cases,Path *description_path,Path *solution_path,Ontology *o,FTKBase *domain_model,int heuristic,int selection_mode,int min_precedents,FeatureTerm *others_argument,FeatureTerm *starting_term,FeatureTerm *others_solution);


FeatureTerm *AW_NOOSLearnerBrain::solve_problem_IBA2A(FeatureTerm *problem,Sort *sort,Symbol *my_name,bool test)
{
	int i;
	char *str;
	AW_Message *m;
	Symbol *agent_name;
	List<Symbol> agent_names;
	FeatureTerm *prediction,*JER,*XER,*f,*op,*solution_record,*candidate_solution,*self_solution,*others_solution;
	FeatureTerm *new_self_JER,*new_other_JER;
	List<FeatureTerm> individual_solutions;
	List<FeatureTerm> JERs;
	List< List< FeatureTerm> > XERs;
	List<FeatureTerm> self_JERs_historic,others_JERs_historic;
	List<float> JER_Cs;
	int current_round;
	bool new_argument;

	/* Retrieve the set of other agents: */ 
	pthread_mutex_lock(&m_sensor_data_mutex);
	m_sensor_data.Rewind();
	while(m_sensor_data.Iterate(f)) {						
		if (f->getSort()->is_a(m_ontology->get_sort("sensed-agent")) &&
			!f->getSort()->is_a(m_ontology->get_sort("sensed-self"))) {
			Symbol *name=f->featureValueQuick("name")->getSymbol();
			if (name->get()[0]=='L' &&
				name->get()[1]=='e' &&
				name->get()[2]=='a' &&
				name->get()[3]=='r' &&
				name->get()[4]=='n' &&
				name->get()[5]=='e' &&
				name->get()[6]=='r') {
				agent_names.Add(new Symbol(name));
			} /* if */ 		
		} /* if */ 
	} /* while */ 
	pthread_mutex_unlock(&m_sensor_data_mutex);

	/* Choose one of the other agents to argue with: */ 
	agent_name=agent_names[0];

	/* Send the problem to the other agent: */ 
	{
		f=new FeatureTerm((char *)0,m_ontology->get_sort("request-individual-single-predict-message"),1,
						  "problem",new FeatureTerm(problem));

		str=f->toStringNOOS(m_memory);
		op=new FeatureTerm((char *)0,m_ontology->get_sort("send-operator"),2,
						   "receiver",new FeatureTerm(0,new Symbol(agent_name),m_ontology),
						   "content",new FeatureTerm(0,new Symbol(str),m_ontology));
		delete []str;
		delete f;
		enqueue_operator(op);
	}

	/* Solve the problem individually: */ 
	individual_solutions.Insert(build_single_jer(problem,sort,my_name));
	self_JERs_historic.Add(new FeatureTerm(individual_solutions[0]));
	self_solution=individual_solutions[0]->featureValue("solution");

	/* Receive the solution from the other: */ 
	{
		wait_message();
		m=get_message();
		prediction=FeatureTerm::fromStringNOOS(m->content->get(),m_memory,m_ontology);
		individual_solutions.Add(prediction);
		others_JERs_historic.Add(new FeatureTerm(individual_solutions[1]));
		others_solution=individual_solutions[1]->featureValue("solution");
		delete m;
	}

	/* Show initial arguments: */ 
	{
		int i;
		FeatureTerm *f;
		char *s;

		i=0;
		individual_solutions.Rewind();
		while(individual_solutions.Iterate(prediction)) {
			f=prediction->featureValueQuick("solution");
			write_debug_report("JER %i, for solution %s\n",i,f->getName()->get());
			f=prediction->featureValueQuick("justification");
			s=f->toStringNOOS(m_memory);
			write_debug_report("%s\n",s);
			delete []s;
			i++;
		} /* while */ 
	}


	current_round=0;
	do{
		write_debug_report("\n[Learner]: IBA2A round %i [%i,%i].\n",current_round,self_JERs_historic.Length(),others_JERs_historic.Length());
		new_self_JER=0;
		new_other_JER=0;

		/* Construct the list of JERs: */ 
		individual_solutions.Rewind();
		JERs.Delete();
		XERs.Delete();
		JER_Cs.Delete();
		while(individual_solutions.Iterate(prediction)) {
			if (prediction->isFeatureTerm()) {
				JERs.Add(new FeatureTerm(prediction));
				XERs.Add(new List<FeatureTerm>);
			} else {
				List<FeatureTerm> *l;
				l=prediction->set_values_quick();
				l->Rewind();
				while(l->Iterate(prediction)) {
					JERs.Add(new FeatureTerm(prediction));
					XERs.Add(new List<FeatureTerm>);
				} /* while */ 
			} /* if */ 
		} /* while */ 

		/* Request XERs from all the other agents for all the JERs: */ 
		{
			FeatureTerm *JER_set=FeatureTerm::SetFeatureTerm((Symbol *)0);
			Symbol *s_tmp;

			JERs.Rewind();
			while(JERs.Iterate(JER)) {
				JER_set->addSetValue(new FeatureTerm(JER));
			} /* while */ 
			f=new FeatureTerm((char *)0,m_ontology->get_sort("request-examination-message"),2,
							  "problem",new FeatureTerm(problem),
							  "prediction",new FeatureTerm(JER_set));
			str=f->toStringNOOS(m_memory);
			s_tmp=new Symbol(str);

			/* Send the JERs: */ 
			op=new FeatureTerm((char *)0,m_ontology->get_sort("send-operator"),2,
							   "receiver",new FeatureTerm(0,new Symbol(agent_name),m_ontology),
							   "content",new FeatureTerm(0,new Symbol(s_tmp),m_ontology));
			enqueue_operator(op);

			delete s_tmp;
			delete []str;
			delete f;
			delete JER_set;
		}

		/* Locally examine the JERs: */ 
		JERs.Rewind();
		while(JERs.Iterate(JER)) {
			XER=examine_JER(JER,sort);
			i=JERs.Position(JER);
			if (i!=-1) {
				XERs[i]->Add(XER);
			} else {
			} /* if */ 
		} /* while */ 

		/* Receive the examination from all the other agents: */ 
		{
			int j;
			/* Wait for the examinations of the agent: */ 
			wait_message();
			m=get_message();
			
			f=FeatureTerm::fromStringNOOS(m->content->get(),m_memory,m_ontology);
			if (f->isFeatureTerm()) {
				XERs[0]->Add(new FeatureTerm(f));
			} else {
				List<FeatureTerm> *l;
				l=f->set_values_quick();
				j=0;
				l->Rewind();
				while(l->Iterate(XER)) {
					XERs[j]->Add(new FeatureTerm(XER));
					j++;
				} /* while */ 
			} /* if */ 	
			delete f;
			delete m;
		}

		for(i=0;i<JERs.Length();i++) {
			int j;
			write_debug_report("For JER%i (%s): ",i,JERs[i]->featureValueQuick("solution")->getName()->get());
			for(j=0;j<XERs[i]->Length();j++) {
				write_debug_report("(%i,%i) ",XERs[i]->operator [](j)->featureValueQuick("endorsing-cases")->getInt(),
											  XERs[i]->operator [](j)->featureValueQuick("counter-examples")->getInt());
			} /* for */ 
			write_debug_report("\n");
		} /* for */ 

		/* Compute the confidence values of the JERs: */ 
		for(i=0;i<JERs.Length();i++) {
			int Y=0,N=0;
			int j;
			for(j=0;j<XERs[i]->Length();j++) {
				Y+=XERs[i]->operator [](j)->featureValueQuick("endorsing-cases")->getInt();
				N+=XERs[i]->operator [](j)->featureValueQuick("counter-examples")->getInt();
			} /* for */ 
			JER_Cs.Add(new float(float(Y)/float(1+Y+N)));
		} /* for */ 

		for(i=0;i<JER_Cs.Length();i++) {
			write_debug_report("C(%i) = %g\n",i,*JER_Cs[i]);
		} /* for */ 


		/* Aggregate the solutions: */ 
		{
			int pos;
			List<FeatureTerm> solutions;
			List<float> votes;
			FeatureTerm *solution;
			int selected;
			float ratio=0;

			for(i=0;i<JERs.Length();i++) {
				solution=JERs[i]->featureValueQuick("solution");
				pos=solutions.Position(solution);
				if (pos==-1) {
					solutions.Add(new FeatureTerm(solution));
					votes.Add(new float(*JER_Cs[i]));
				} else {
					*votes[pos]=*votes[pos]+*JER_Cs[i];
				} /* if */ 		
			} /* for */ 

			for(i=0;i<solutions.Length();i++) {
				write_debug_report("%s -> %g\n",solutions[i]->getName()->get(),*votes[i]);
			} /* for */ 

			selected=0;
			for(i=0;i<solutions.Length();i++) {
				ratio+=*votes[i];
				if (*votes[i]>*votes[selected]) {
					selected=i;
				} /* if */ 
			} /* for */ 

			ratio=(*votes[selected])/ratio;

			candidate_solution=new FeatureTerm(solutions[selected]);
			write_debug_report("< The candidate solution in round t = %i is: %s (%g)>\n",current_round,candidate_solution->getName()->get(),ratio);
			write_debug_report("My own predicted solution is: %s\n",self_solution->getName()->get());
			write_debug_report("The other's predicted solution is: %s\n",others_solution->getName()->get());
		}

		new_argument=false;

		if (*self_solution==*candidate_solution) {
			/* I AGREE WITH THE CURRENT RESULT: */ 
			write_debug_report("I agree with the current result\n");
			if (*others_solution==*candidate_solution) {
				/* THE OTHER AGENT AGREES WITH THE CURRENT RESULT: */ 
				write_debug_report("The other agent agrees with the current result\n");
			} else {
				/* THE OTHER AGENT DISAGREES WITH THE CURRENT RESULT: */ 
				write_debug_report("The other agent disagrees with the current result\n");

				/* Request a counterargument to my argument: */ 
				/* Send the counterexample: */ 
				{
					f=new FeatureTerm((char *)0,m_ontology->get_sort("request-counterargument"),2,
									  "jer",new FeatureTerm(individual_solutions[0]),
									  "problem",new FeatureTerm(problem));

					if (!others_JERs_historic.EmptyP()) {
						if (others_JERs_historic.Length()==1) {
							f->defineFeatureValue("historic",new FeatureTerm(others_JERs_historic[0]));
						} else {
							FeatureTerm *h,*f2;
							h=FeatureTerm::SetFeatureTerm((Symbol *)0);
							others_JERs_historic.Rewind();
							while(others_JERs_historic.Iterate(f2)) {
								h->addSetValue(new FeatureTerm(f2));
							} /* while */ 
							f->defineFeatureValue("historic",h);
						} /* if */ 
					}

					str=f->toStringNOOS(m_memory);
					op=new FeatureTerm((char *)0,m_ontology->get_sort("send-operator"),2,
									   "receiver",new FeatureTerm(0,new Symbol(agent_name),m_ontology),
									   "content",new FeatureTerm(0,new Symbol(str),m_ontology));
					delete []str;
					delete f;
					enqueue_operator(op);
				}

				/* Wait for response: */ 
				{
					wait_message();
					m=get_message();

					f=FeatureTerm::fromStringNOOS(m->content->get(),m_memory,m_ontology);
					delete m;
				}

				if (f->getSort()==m_ontology->get_sort("problem-prediction")) {

					/* Received a counterargument consisting on a JER: */ 
					if (new_other_JER!=0) delete new_other_JER;
					new_other_JER=f;				
				} else {
					if (f->getSort()==m_ontology->get_sort("any")) {
						/* The other agent cannot find any counterargument: */ 
						write_debug_report("The other agent surrenders, since it cannot find any counterargument\n");
						delete f;
					} else {
						/* Received a counterargument consisting on a counterexample: */ 
						write_debug_report("[Learner]: learnt case %s\n",f->getName()->get());
						received_cases_IBA2A.Add(f);

						if (new_self_JER!=0) delete new_self_JER;
						new_self_JER=build_single_jer(problem,sort,my_name);				
					} /* if */ 
				} /* if */ 

			} /* if */ 
		} else {
			/* I DISAGREE WITH THE CURRENT RESULT: */ 
			FeatureTerm *counterargument,*counterexample;

			write_debug_report("I disagree with the current result\n");

			counterargument=generate_attacking_JER(problem,individual_solutions[1],&self_JERs_historic,sort,my_name);

			if (counterargument!=0) {
				write_debug_report("I've been able to find a counterargument consisting in a JER\n");			
				if (new_self_JER!=0) delete new_self_JER;
				new_self_JER=counterargument;				
			} else {
				counterexample=generate_counterexample(&already_sent_cases_IBA2A,individual_solutions[1],sort);

				if (counterexample!=0) {
					write_debug_report("I've been able to find a counterargument consisting in a counterexample: %s\n",counterexample->getName()->get());

					/* Send the counterexample: */ 
					{
						f=new FeatureTerm((char *)0,m_ontology->get_sort("learn-message-iba"),1,
										  "case",new FeatureTerm(counterexample));

						str=f->toStringNOOS(m_memory);
						op=new FeatureTerm((char *)0,m_ontology->get_sort("send-operator"),2,
										   "receiver",new FeatureTerm(0,new Symbol(agent_name),m_ontology),
										   "content",new FeatureTerm(0,new Symbol(str),m_ontology));
						delete []str;
						delete f;
						enqueue_operator(op);
					}

					/* Wait for confirmation: */ 
					wait_message();
					m=get_message();
					delete m;

					/* Send the problem again: */ 
					{
						f=new FeatureTerm((char *)0,m_ontology->get_sort("request-individual-single-predict-message"),1,
										  "problem",new FeatureTerm(problem));

						str=f->toStringNOOS(m_memory);
						op=new FeatureTerm((char *)0,m_ontology->get_sort("send-operator"),2,
										   "receiver",new FeatureTerm(0,new Symbol(agent_name),m_ontology),
										   "content",new FeatureTerm(0,new Symbol(str),m_ontology));
						delete []str;
						delete f;
						enqueue_operator(op);
					}					

					/* Wait for answer: */ 
					{
						wait_message();	
						m=get_message();

						if (new_other_JER!=0) delete new_other_JER;
						new_other_JER=FeatureTerm::fromStringNOOS(m->content->get(),m_memory,m_ontology);
						delete m;
					}

					delete counterexample;
				} else {
					write_debug_report("I surrender, since I cannot find any counterargument\n");
				} /* if */ 				
			} /* if */ 
		} /* if */ 

		if (new_self_JER!=0) {
			bool found=false;
			FeatureTerm *f;
			self_JERs_historic.Rewind();
			while(self_JERs_historic.Iterate(f) && !found) {
				if (f->equivalents(new_self_JER)) {
					found=true;
				} /* if */ 
			} /* while */ 

			if (found) {
				write_debug_report("Argumentation stopped since I've repeated a JER!\n");
				delete new_self_JER;
				new_self_JER=0;
			} else {
				/* Show new argument: */ 
				{
					FeatureTerm *f;
					char *s;
					f=new_self_JER->featureValueQuick("solution");
					write_debug_report("JER 0, for solution %s\n",f->getName()->get());
					f=new_self_JER->featureValueQuick("justification");
					s=f->toStringNOOS(m_memory);
					write_debug_report("%s\n",s);
					delete []s;
				}

				JER=individual_solutions.ExtractIni();
				delete JER;
				individual_solutions.Insert(new_self_JER);
				self_JERs_historic.Add(new FeatureTerm(new_self_JER));
				new_self_JER=0;
				self_solution=individual_solutions[0]->featureValue("solution");
				new_argument=true;
			} /* if */ 
		} /* if */ 		
		if (new_other_JER!=0) {
			bool found=false;
			FeatureTerm *f;
			others_JERs_historic.Rewind();
			while(others_JERs_historic.Iterate(f) && !found) {
				if (f->equivalents(new_other_JER)) {
					found=true;
				} /* if */ 
			} /* while */ 

			if (found) {
				write_debug_report("Argumentation stopped since the other has repeated a JER!\n");
				delete new_other_JER;
				new_other_JER=0;
			} else {
				/* Show new argument: */ 
				{
					FeatureTerm *f;
					char *s;
					f=new_other_JER->featureValueQuick("solution");
					write_debug_report("JER 1, for solution %s\n",f->getName()->get());
					f=new_other_JER->featureValueQuick("justification");
					s=f->toStringNOOS(m_memory);
					write_debug_report("%s\n",s);
					delete []s;
				}

				JER=individual_solutions.Extract();
				delete JER;
				individual_solutions.Add(new_other_JER);
				others_JERs_historic.Add(new FeatureTerm(new_other_JER));
				new_other_JER=0;
				others_solution=individual_solutions[1]->featureValue("solution");
				new_argument=true;
			} /* if */ 
		} /* if */ 

		current_round++;
	}while(new_argument);


	/* Reset the protocol, so that the next time it will start all over again: */ 
	{
		FeatureTerm *f,*op;
		char *str;

		/* Appply retention strategy (only if the problem is not a TEST problem): */ 
		if (!test) retain_cases(&received_cases_IBA2A,m_amal2_retention_strategy);

		already_sent_cases_IBA2A.Delete();
		received_cases_IBA2A.Delete();

		if (test) f=new FeatureTerm((char *)0,m_ontology->get_sort("reset-test-iba"),0);
			 else f=new FeatureTerm((char *)0,m_ontology->get_sort("reset-iba"),0);

		str=f->toStringNOOS(m_memory);
		op=new FeatureTerm((char *)0,m_ontology->get_sort("send-operator"),2,
						   "receiver",new FeatureTerm(0,new Symbol(agent_name),m_ontology),
						   "content",new FeatureTerm(0,new Symbol(str),m_ontology));
		delete []str;
		delete f;
		enqueue_operator(op);

		wait_message();	
		m=get_message();
		delete m;
	}

	solution_record=new FeatureTerm((char *)0,m_ontology->get_sort("problem-prediction"),1,
									"solution",candidate_solution);
	return solution_record;
} /* AW_NOOSLearnerBrain::solve_problem_IBA2A */ 


FeatureTerm *AW_NOOSLearnerBrain::generate_attacking_JER(FeatureTerm *problem,FeatureTerm *others_JER,List<FeatureTerm> *historic,Sort *sort,Symbol *agent_name)
{
	Prediction *p;
	List<FeatureTerm> *cases=0;
	Path dp,sp;
	FeatureTerm *others_d=0,*c;

	if (!m_generate_counterarguments) return 0;

	/* Retrieve precedents: */ 
	cases=m_casebase->SearchFT(sort);

	received_cases_IBA2A.Rewind();
	while(received_cases_IBA2A.Iterate(c)) cases->Add(c);

	write_debug_report("[%s]: generating attacking JER with %i [historic: %i] cases\n",agent_name->get(),cases->Length(),historic->Length());

	/* Build description and solution paths: */ 
	dp.Add(new Symbol("description"));
	sp.Add(new Symbol("solution"));
	if (strcmp(problem->getSort()->get(),"sponge")==0) {
		sp.Add(new Symbol("order"));
	} /* if */ 

	others_d=others_JER->featureValueQuick("justification");

	p=eva_LID_argumentation(problem,cases,&dp,&sp,m_ontology,m_memory,1,0,1,
							others_d, /* has to be more specific than this */ 
							others_d, /* starting term */ 
							others_JER->featureValueQuick("solution"));
	while(!cases->EmptyP()) cases->ExtractIni();
	delete cases;

	/* Determine whether the JER is valid: */ 
	if (p!=0) {
		if (!p->justifications.EmptyP()) {
			int selected=0;
			int i;
			char *s;
	
			for(i=0;i<p->solutions.Length();i++) {
				if (*p->support[i]>*p->support[selected]) selected=i;
			} /* if */ 

			s=p->solutions[selected]->toStringNOOS(m_memory);
			write_debug_report("JER generated: %i solutions, selected: %s [%i,%s,%s]\n",
								p->solutions.Length(),s,
								*p->support[selected],
								(others_d->subsumes(p->justifications[selected]) ? "true":"false"),
								(p->justifications[selected]->subsumes(others_d) ? "true":"false"));
			delete []s;

			if (*p->support[selected]>0) {
				if (*p->solutions[selected]==*others_JER->featureValueQuick("solution") ||
					(others_d->subsumes(p->justifications[selected]) &&
					 !p->justifications[selected]->subsumes(others_d))) {
					FeatureTerm *counterargument=0;
					bool found=false;
					FeatureTerm *f;

					/* valid counterargument: */ 
					counterargument=new FeatureTerm((char *)0,m_ontology->get_sort("problem-prediction"),4,
													"solution",new FeatureTerm(p->solutions[selected]),
													"support",new FeatureTerm((char *)0,*p->support[selected],m_ontology),
													"justification",new FeatureTerm(p->justifications[selected]),
													"agent",new FeatureTerm(0,new Symbol(agent_name),m_ontology));

					if (historic!=0) {
						historic->Rewind();
						while(historic->Iterate(f) && !found) {
							if (f->equivalents(counterargument)) {
								found=true;
							} /* if */ 
						} /* while */ 
					} /* if */ 

					if (!found) {
						delete p;
						return counterargument;
					} else {
						delete counterargument;
					} /* if */ 
				} /* if */ 
			} /* if */ 
		} /* if */ 

		delete p;
		return 0;
	} /* if */ 

	return 0;
} /* AW_NOOSLearnerBrain::generate_attacking_JER */ 


FeatureTerm *AW_NOOSLearnerBrain::generate_counterexample(List<FeatureTerm> *not_allowed,FeatureTerm *others_JER,Sort *sort)
{
	FeatureTerm *solution=others_JER->featureValueQuick("solution");
	FeatureTerm *d=others_JER->featureValueQuick("justification");
	FeatureTerm *counterexample=0,*c,*cs,*cd;
	List<FeatureTerm> *cases;

	if (!m_generate_counterexamples) return 0;

	cases=m_casebase->SearchFT(sort);

	received_cases_IBA2A.Rewind();
	while(received_cases_IBA2A.Iterate(c)) cases->Add(c);

	while(!cases->EmptyP() && counterexample==0) {
		c=cases->ExtractIni();
		cd=get_description(c);
		cs=get_solution(c);
		if (d->subsumes(cd) && !not_allowed->MemberP(c) && !(*cs==*solution)) {
			counterexample=new FeatureTerm(c);
		} /* if */ 
		delete cd;
		delete cs;
	} /* while */ 

	while(!cases->EmptyP()) cases->ExtractIni();
	delete cases;

	if (counterexample!=0) not_allowed->Add(new FeatureTerm(counterexample));

	return counterexample;
} /* AW_NOOSLearnerBrain::generate_counterexample */ 

